package com.dxtan90.judge;

import com.dxtan90.bean.RGB;
import com.dxtan90.gray.impl.AverageMethod;
import com.dxtan90.util.RGBUtil;

import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * @author: shijunhao
 * @date: 2021/12/1 16:55
 * @description: √ 第一步，缩小尺寸为8×8，以去除图片的细节，只保留结构、明暗等基本信息，摒弃不同尺寸、比例带来的图片差异。
 * √ 第二步，简化色彩。将缩小后的图片转为灰度图像。
 * √ 第三步，计算平均值。计算所有像素的灰度平均值。
 * √ 第四步，比较像素的灰度。将64个像素的灰度，与平均值进行比较。大于或等于平均值，记为1；小于平均值，记为0。
 * 第五步，计算哈希值。将上一步的比较结果，组合在一起，就构成了一个64位的整数，这就是这张图片的指纹。
 * 第六步，计算哈希值的差异，得出相似度（汉明距离或者余弦值）。
 * √ 第五步： 直接余弦获取相似度
 * 作者：雾里看花
 * 链接：https://www.zhihu.com/question/430351439/answer/1577420762
 */
public class PictureSameJudge {

    /**
     * @param oriImg: 原图
     * @return: 64像素灰度处理
     * @author: shijunhao
     * @date: 2021/12/1 17:05
     * @description: 将图片转为64像素灰度图
     */
    public BufferedImage doGraySixtyFourPx(BufferedImage oriImg) {
        //64像素处理
        BufferedImage eightImg = new BufferedImage(8, 8, oriImg.getType());
        Graphics g = eightImg.getGraphics();
        g.drawImage(oriImg, 0, 0, eightImg.getWidth(), eightImg.getHeight(), null);
        g.dispose();

        //灰度处理
        AverageMethod averageMethod = new AverageMethod();
        BufferedImage resImage = new BufferedImage(eightImg.getWidth(), eightImg.getHeight(), BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < eightImg.getWidth(); i++) {
            for (int j = 0; j < eightImg.getHeight(); j++) {
                int color = eightImg.getRGB(i, j);
                RGB rgb = RGBUtil.ColorToRGB(color);
                int grey = averageMethod.getGrey(rgb.r, rgb.g, rgb.b);
                int newColor = RGBUtil.RGBToColor(0, grey, grey, grey);
                resImage.setRGB(i, j, newColor);
            }
        }
        return resImage;
    }

    /**
     * @param img: 像素灰度图
     * @return: int[] 图形指纹
     * @author: shijunhao
     * @date: 2021/12/1 17:22
     * @description: 将图片的特征hash值提出
     */
    public int[] getImgHash(BufferedImage img) {
        int[] hash = new int[img.getWidth() * img.getHeight()];

        //取平均值
        int sum = 0;
        for (int i = 0; i < img.getWidth(); i++) {
            for (int j = 0; j < img.getHeight(); j++) {
                int color = img.getRGB(i, j);
                RGB rgb = RGBUtil.ColorToRGB(color);
                sum += rgb.b;
            }
        }
        int average = sum / (img.getHeight() * img.getWidth());

        //取特征值
        for (int i = 0; i < img.getWidth(); i++) {
            for (int j = 0; j < img.getHeight(); j++) {
                int color = img.getRGB(i, j);
                RGB rgb = RGBUtil.ColorToRGB(color);
                hash[i * img.getHeight() + j] = rgb.b > average ? 1 : 0;
            }
        }
        return hash;
    }

    /**
     * @param imgHash1: 图像1hash值
     * @param imgHash2: 图像2hash值
     * @return: double 余弦相似度
     * @author: shijunhao
     * @date: 2021/12/1 17:37
     * @description: 获取两张图片的相似度
     */
    public double getSimilarity(int[] imgHash1, int[] imgHash2) {
        if (imgHash1.length > imgHash2.length)
            return getSimilarity(imgHash2, imgHash1);
        int product = 0;
        int and1 = 0;
        int and2 = 0;
        for (int i = 0; i < imgHash1.length; i++) {
            product += imgHash1[i] * imgHash2[i];
            and1 += imgHash1[i] * imgHash1[i];
            and2 += imgHash2[i] * imgHash2[i];
        }
        for (int i = imgHash1.length; i < imgHash2.length; i++) {
            and2 += imgHash2[i] * imgHash2[i];
        }
        return product / (Math.sqrt(and1) * Math.sqrt(and2));
    }

    public double getSimilarity(BufferedImage oriImg, BufferedImage tarImg) {
        BufferedImage img1 = doGraySixtyFourPx(oriImg);
        BufferedImage img2 = doGraySixtyFourPx(tarImg);
        int[] imgHash1 = getImgHash(img1);
        int[] imgHash2 = getImgHash(img2);
        return getSimilarity(imgHash1, imgHash2);
    }
}
